// *** LICENSE HEADER ***
// Filename: ./game.cpp
// This file is part of Nodewarz 0.1
// Nodewarz is a 3d OpenGL strategy game
// Copyright (C) 2007 CZestmyr
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// *** END LICENSE HEADER ***
#include "game.h"

game::game(engine* newParent) {
    players = 0;
    parent = newParent;
    theCamera = new camera;
    theBar = new statusBar(this);
    theParticleSystem = new particleSystem;
    theBoard = new gameBoard;
    theLightManager = new lightManager;
    theResources = new resourceList(this);

    theLightManager->SetGameBoardPointer(theBoard);
    theBoard->SetLightManager(theLightManager);

    rightDrag = false;
    cursor3DXPos = cursor3DYPos = 0.0f;
}

game::~game() {
    if (players)
        delete players;
    delete myGui;
    delete theCamera;
    delete theBar;
    delete theResources;
    delete theParticleSystem;
    delete theBoard;
    delete theLightManager;
}

void game::NewGame(playerList* newPlayerlist) {
    theBoard->LoadTerrainTexture("rock.jpg");
    theBoard->Redim(50, 50);
    if (newPlayerlist == NULL) {
        players = new playerList(this);
        players->CreateDefaultPlayers();
    } else
        players = newPlayerlist;

    //Resources creation
    theResources->AddResource(-7.0f, 13.0f, RT_ATTACK, _RESOURCE_RADIUS, 10);
    theResources->AddResource(-13.0f, -7.0f, RT_ATTACK, _RESOURCE_RADIUS, 10);
    theResources->AddResource(7.0f, -13.0f, RT_ATTACK, _RESOURCE_RADIUS, 10);
    theResources->AddResource(13.0f, 7.0f, RT_ATTACK, _RESOURCE_RADIUS, 10);

    theResources->AddResource(7.0f, 13.0f, RT_DEFENCE, _RESOURCE_RADIUS, 10);
    theResources->AddResource(13.0f, -7.0f, RT_DEFENCE, _RESOURCE_RADIUS, 10);
    theResources->AddResource(-7.0f, -13.0f, RT_DEFENCE, _RESOURCE_RADIUS, 10);
    theResources->AddResource(-13.0f, 7.0f, RT_DEFENCE, _RESOURCE_RADIUS, 10);

    theResources->AddResource(-23.0f, 0.0f, RT_CREDITS, _RESOURCE_RADIUS, 25);
    theResources->AddResource(23.0f, 0.0f, RT_CREDITS, _RESOURCE_RADIUS, 25);
    theResources->AddResource(0.0f, -23.0f, RT_CREDITS, _RESOURCE_RADIUS, 25);
    theResources->AddResource(0.0f, 23.0f, RT_CREDITS, _RESOURCE_RADIUS, 25);
    theResources->AddResource(-19.0f, -19.0f, RT_CREDITS, _RESOURCE_RADIUS, 25);
    theResources->AddResource(19.0f, -19.0f, RT_CREDITS, _RESOURCE_RADIUS, 25);
    theResources->AddResource(-19.0f, 19.0f, RT_CREDITS, _RESOURCE_RADIUS, 25);
    theResources->AddResource(19.0f, 19.0f, RT_CREDITS, _RESOURCE_RADIUS, 25);

    theResources->AddResource(0.0f, -5.0f, RT_TAKEOVER, _RESOURCE_RADIUS, 15);
    theResources->AddResource(0.0f, 5.0f, RT_TAKEOVER, _RESOURCE_RADIUS, 15);
    theResources->AddResource(5.0f, 0.0f, RT_LIVES, _RESOURCE_RADIUS, 15);
    theResources->AddResource(-5.0f, 0.0f, RT_LIVES, _RESOURCE_RADIUS, 15);
}

void game::InitGame() {
    myGui = new gui(parent);
    myGui->SetCallback(engine::GuiCallback);

    if (players)
        players->NextPlayer(true);
    theCamera->Reset();

    //Hack - recalculate lighting on the whole board
    theBoard->RecalculateLighting(0.0, 0.0, 1000.0);

    //Used for cursor efects
    timestamp = 0;

    //game gui creation
    char fpsbuffer[16];
    sprintf(fpsbuffer, "FPS: %i", parent->GetFPS());
    myGui->CreateLabel(10, 10, 20, fpsbuffer, _BUTTON_FPSLABEL);

    myGui->CreateCanvas(_WIDTH - 160, 0, 160, _HEIGHT);
    myGui->CreateTextField(10, _HEIGHT - 50, _WIDTH - 20, 20, "", 16, 128, _BUTTON_MESSAGE_LINE);
    myGui->GetElement(_BUTTON_MESSAGE_LINE)->Disable();
    myGui->CreateButton(_WIDTH - 150, 10, 140, 20, "Info tool [F2]", _BUTTON_MODENORMAL);

    //Node information labels
    myGui->CreateLabel(_WIDTH - 140, 40, 12, "Node health:", _BUTTON_GAMELABEL);
    myGui->CreateLabel(_WIDTH - 140, 55, 12, "Attack:", _BUTTON_GAMELABEL);
    myGui->CreateLabel(_WIDTH - 140, 70, 12, "Defence:", _BUTTON_GAMELABEL);
    myGui->CreateLabel(_WIDTH - 140, 85, 12, "Takeover:", _BUTTON_GAMELABEL);
    myGui->CreateLabel(_WIDTH - 70, 40, 12, "N/A", _BUTTON_GAMELABEL1);
    myGui->CreateLabel(_WIDTH - 70, 55, 12, "N/A", _BUTTON_GAMELABEL2);
    myGui->CreateLabel(_WIDTH - 70, 70, 12, "N/A", _BUTTON_GAMELABEL3);
    myGui->CreateLabel(_WIDTH - 70, 85, 12, "N/A", _BUTTON_GAMELABEL4);
    myGui->CreateColorViewer(_WIDTH - 45, 40, 25, 60, _BUTTON_GAMENODECOLOR);
    myGui->CreateButton(_WIDTH - 150, 110, 140, 20, "Connection tool [F3]", _BUTTON_MODESELECT);
    myGui->CreateButton(_WIDTH - 150, 135, 140, 20, "Add another node [F4]", _BUTTON_MODEADD);
    myGui->CreateButton(_WIDTH - 150, 160, 140, 20, "Connect selected", _BUTTON_GAMECONNECT);
    myGui->CreateButton(_WIDTH - 150, 190, 140, 20, "ATTACK [F5] ($3)", _BUTTON_MODEATTACK);
    myGui->CreateButton(_WIDTH - 150, 215, 140, 20, "DEFEND [F6] ($5)", _BUTTON_MODEDEFEND);
    myGui->CreateButton(_WIDTH - 150, 240, 140, 20, "PSI BURST [F7] ($5)", _BUTTON_MODETAKEOVER);
    myGui->CreateButton(_WIDTH - 150, 280, 140, 20, "DISSINTEGRATE [F10]", _BUTTON_MODEDISSINTEGRATE);
    myGui->CreateToggle(_WIDTH - 150, 320, false, _BUTTON_AREATOGGLE);
    myGui->CreateLabel(_WIDTH - 125, 322, 12, "Show boundaries", _BUTTON_GAMELABEL);
    myGui->CreateToggle(_WIDTH - 150, 345, true, _BUTTON_ENERGYTOGGLE);
    myGui->CreateLabel(_WIDTH - 125, 347, 12, "Show energies", _BUTTON_GAMELABEL);
    myGui->CreateToggle(_WIDTH - 150, 370, false, _BUTTON_AMMOUNTTOGGLE);
    myGui->CreateLabel(_WIDTH - 125, 372, 12, "Show resource ammounts", _BUTTON_GAMELABEL);
    myGui->CreateToggle(_WIDTH - 150, 395, false, _BUTTON_GRIDTOGGLE);
    myGui->CreateLabel(_WIDTH - 125, 397, 12, "Show grid", _BUTTON_GAMELABEL);
    myGui->CreateButton(_WIDTH - 150, 420, 140, 20, "Reset Camera", _BUTTON_RESETCAMERA);
    myGui->CreateButton(_WIDTH - 150, _HEIGHT - 30, 140, 20, "Quit game", _BUTTON_ENDGAME);
    myGui->CreateButton(_WIDTH - 150, _HEIGHT - 100, 140, 30, "End turn", _BUTTON_ENDTURN);
}

void game::EndGame() {
    if (players)
        delete players;
    players = 0;
    theBoard->UnloadTerrainTexture();
    theLightManager->FlushLights();

    delete myGui;
    myGui = 0;
}

void game::Update(Uint32 dt) {
    theCamera->Update(dt);
    theParticleSystem->Update(dt);

    timestamp += dt;
}

void game::Draw() {
    //Set the camera angle
    theCamera->Draw();

    //Draw the game board (with or without grid)
    bool showGrid = false;
    toggle* tg = (toggle*)myGui->GetElement(_BUTTON_GRIDTOGGLE);
    if (tg)
        showGrid = tg->GetToggled();
    theBoard->Draw(showGrid);

    //Picking cursor
    SDL_Color pc;
    pc = players->GetActivePlayerPointer()->GetColor();
    glColor3ub(pc.r, pc.g, pc.b);

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glBlendFunc(GL_ONE, GL_ONE);

    glBindTexture(GL_TEXTURE_2D, cursorTexture);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(cursor3DXPos - 0.5f, cursor3DYPos + 0.5f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(cursor3DXPos - 0.5f, cursor3DYPos - 0.5f, 0.0f);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(cursor3DXPos + 0.5f, cursor3DYPos - 0.5f, 0.0f);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(cursor3DXPos + 0.5f, cursor3DYPos + 0.5f, 0.0f);
    glEnd();

    glBindTexture(GL_TEXTURE_2D, cursorTexture2);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 1.0f);
    float size = sin(timestamp / 200.0f) / 4.0f + 1.0f;
    float cosfactor = cos(timestamp / 1000.0f) * size;
    float sinfactor = sin(timestamp / 1000.0f) * size;
    glVertex3f(cursor3DXPos + cosfactor, cursor3DYPos + sinfactor, 0.1f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(cursor3DXPos - sinfactor, cursor3DYPos + cosfactor, 0.1f);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(cursor3DXPos - cosfactor, cursor3DYPos - sinfactor, 0.1f);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(cursor3DXPos + sinfactor, cursor3DYPos - cosfactor, 0.1f);
    glEnd();

    glEnable(GL_DEPTH_TEST);

    //Draw game resource radii
    bool showBoundaries = false;
    tg = (toggle*)myGui->GetElement(_BUTTON_AREATOGGLE);
    if (tg)
        showBoundaries = tg->GetToggled();
    if (showBoundaries)
        theResources->DrawRadii();

    //Draw game resource ammounts
    bool drawAmmounts = false;
    tg = (toggle*)myGui->GetElement(_BUTTON_AMMOUNTTOGGLE);
    if (tg)
        drawAmmounts = tg->GetToggled();

    theResources->Draw(drawAmmounts);

    //Draw the players
    if (players)
        players->Draw();

    //Draw the status bar
    theBar->Draw();

    //Draw particles
    theParticleSystem->Draw();

    //Draw the gui
    myGui->Draw();
}

void game::LoadTextures() {
    cursorTexture = LoadTexture("cursor.png");
    cursorTexture2 = LoadTexture("cursor2.png");
    theParticleSystem->LoadParticleTexture(0, "particle.png");
    theParticleSystem->LoadParticleTexture(1, "selection.png");
    theParticleSystem->LoadParticleTexture(2, "dollar.png");
    playerList::LoadTextures();
}

void game::UnloadTextures() {
    UnloadTexture(cursorTexture);
    UnloadTexture(cursorTexture2);
    playerList::UnloadTextures();
}

engine* game::GetEngine() {
    return parent;
}

playerList* game::GetPlayers() {
    return players;
}

statusBar* game::GetStatusBar() {
    return theBar;
}

particleSystem* game::GetParticleSystem() {
    return theParticleSystem;
}

lightManager* game::GetLightManager() {
    return theLightManager;
}

gui* game::GetGui(){
    return myGui;
}

gameBoard* game::GetBoard(){
    return theBoard;
}

resourceList* game::GetResources(){
    return theResources;
}

camera* game::GetCamera(){
    return theCamera;
}

void game::MouseClickEvent(int xCoordinate, int yCoordinate, bool right) {
    if (xCoordinate > _WIDTH - 160){
        myGui->Click(xCoordinate, yCoordinate);
        return;
    }
    if (right) rightDrag = true;
    else
        players->MouseClickEvent(xCoordinate, yCoordinate, right);
}

void game::MouseReleaseEvent(int xCoordinate, int yCoordinate, bool right) {
    if (xCoordinate > _WIDTH - 160){
        myGui->Release(xCoordinate, yCoordinate);
    }
    if (right) rightDrag = false;
}

void game::MouseMoveEvent(int newX, int newY, int relX, int relY) {
    if (rightDrag)
        theCamera->Move(relX * 0.265f, relY * 0.265f);

    if (newX > _WIDTH - 160)
        SDL_ShowCursor(SDL_ENABLE);
    else
        SDL_ShowCursor(SDL_DISABLE);

    PickAtZPlane(cursor3DXPos, cursor3DYPos, newX, newY);

    players->SetPickingCursorPosition(cursor3DXPos, cursor3DYPos);
}

void game::KeyPressEvent(SDLKey key, char c) {
    if (myGui->GetElement(_BUTTON_MESSAGE_LINE) && myGui->GetElement(_BUTTON_MESSAGE_LINE)->IsEnabled()){
        myGui->KeyPress(key, c);
        return;
    }
    switch (key) {
        case SDLK_LEFT:
            theCamera->RotateLeft();
            break;
        case SDLK_RIGHT:
            theCamera->RotateRight();
            break;
        case SDLK_UP:
            theCamera->ZoomIn();
            break;
        case SDLK_DOWN:
            theCamera->ZoomOut();
            break;
        case SDLK_a:
            theCamera->RotateDown();
            break;
        case SDLK_q:
            theCamera->RotateUp();
            break;
        default:
            players->KeyPressEvent(key, (char)key);
            break;
    }
}

void game::KeyReleaseEvent(SDLKey key) {
    if (myGui->GetElement(_BUTTON_MESSAGE_LINE) && myGui->GetElement(_BUTTON_MESSAGE_LINE)->IsEnabled()){
        if (key == SDLK_ESCAPE) {
            myGui->GetElement(_BUTTON_MESSAGE_LINE)->Disable();
            myGui->SetElementText("", _BUTTON_MESSAGE_LINE);
        }
        return;
    }

    switch (key) {
        case SDLK_LEFT:
            theCamera->EndRotateLeft();
            break;
        case SDLK_RIGHT:
            theCamera->EndRotateRight();
            break;
        case SDLK_q:
            theCamera->EndRotateUp();
            break;
        case SDLK_a:
            theCamera->EndRotateDown();
            break;
        case SDLK_UP:
        case SDLK_DOWN:
            theCamera->ZoomStop();
            break;
        //Talk textbox
        case SDLK_t:
            if (myGui->GetElement(_BUTTON_MESSAGE_LINE))
                if (!myGui->GetElement(_BUTTON_MESSAGE_LINE)->IsEnabled()) {
                    myGui->GetElement(_BUTTON_MESSAGE_LINE)->Enable();
                    myGui->Activate(_BUTTON_MESSAGE_LINE);
                } else
                    myGui->GetElement(_BUTTON_MESSAGE_LINE)->Disable();
            break;
        case SDLK_n:
            if (players->NextPlayer()) {
                parent->GetConnector()->PrepareEvent(_CONNECTOR_EVENT_ENDTURN);
                parent->GetConnector()->SendEvent();
            }
            break;
        default:
            players->KeyReleaseEvent(key);
            break;
    }
}
